package com.lagou.server;

import com.lagou.Mapper;
import com.lagou.servlet.Servlet;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import java.io.File;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

/**
 * 负责解析server.xml的。
 */
public class MinicatEngine {

    // 缓存urlClassLoader
    private Map<URL, URLClassLoader> urlClassLoaderMap = new ConcurrentHashMap<>();

    /**
     * 解析server.xml配置文件
     * @return
     * @throws Exception
     */
    public Mapper loadServer() throws Exception {
        InputStream stream = Thread.currentThread().getContextClassLoader().getResourceAsStream("server.xml");
        SAXReader reader = new SAXReader();
        Document document = reader.read(stream);
        Element root = document.getRootElement();
        List<Element> serviceList = root.selectNodes("Service");
        List<Mapper.Host> hostList = new ArrayList<>();
        for (Element e : serviceList) {
            Element engine = (Element) e.selectSingleNode("Engine");
            List<Element> host = engine.elements("Host");
            for (Element h : host) {
                String appBase = h.attributeValue("appBase");
                File appFolder = new File(appBase);

                if (appFolder.exists()) {
                    List<Mapper.Context> contexts = Files.list(appFolder.toPath())
                            .peek(item -> {
                        // 先把classloader放入缓存
                        try {
                            URL url = item.toUri().toURL();
                            this.urlClassLoaderMap.put(url, new URLClassLoader(new URL[]{url}));
                        } catch (MalformedURLException e1) {
                            e1.printStackTrace();
                        }
                    }).map(item -> {

                        try {
                            return loadServlet(item, appBase);
                        } catch (Exception e1) {
                            e1.printStackTrace();
                            return null;
                        }
                    }).collect(Collectors.toList());
                    Mapper.Host mapperHost = new Mapper.Host(h.attributeValue("name"), contexts);
                    hostList.add(mapperHost);
                }
            }
        }

        return new Mapper(hostList);
    }

    /**
     * 加载web.xml, 封装成context对象
     * @param path
     * @return
     * @throws Exception
     */
    Mapper.Context loadServlet(Path path, String appBase) throws Exception {
        URL url = path.toUri().toURL();
        URLClassLoader urlClassLoader = urlClassLoaderMap.get(url);

        InputStream stream = Files.newInputStream(Paths.get(path.toString(), "web.xml"));
        SAXReader reader = new SAXReader();
        Document document = reader.read(stream);
        Element root = document.getRootElement();
        List<Element> selectNodes = root.selectNodes("//servlet");
        List<Mapper.Wrapper> wrappers = new ArrayList<>();
        for (Element element : selectNodes) {
            Element servletnameElement = (Element)
                    element.selectSingleNode("servlet-name");
            String servletName = servletnameElement.getStringValue();
            Element servletclassElement = (Element)
                    element.selectSingleNode("servlet-class");
            String servletClass =
                    servletclassElement.getStringValue();
            Element servletMapping = (Element)
                    root.selectSingleNode("/web-app/servlet-mapping[servlet-name='" +
                            servletName + "']");
            String urlPattern = servletMapping.selectSingleNode("url-pattern").getStringValue();
            Class c = urlClassLoader.loadClass(servletClass);
            Servlet servlet = (Servlet) c.getDeclaredConstructor().newInstance();
            wrappers.add(new Mapper.Wrapper(servletName, urlPattern, servlet));
        }

        return new Mapper.Context(path.getFileName().toString(), wrappers, appBase);
    }
}
